using System;
using System.Security.Permissions;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using GeoFramework.IO;
#if !PocketPC || DesignTime
using System.ComponentModel;
#endif
namespace GeoFramework.Gps
{
	/// <summary>
	/// Represents a base class for building a GPS data interpreter.
	/// </summary>
#if !PocketPC || DesignTime
	[ToolboxBitmap("Interpreter.bmp")]
	[Serializable()]
#endif
	public abstract class Interpreter : System.ComponentModel.Component
    {
        #region Private Variables

        private Stream pBaseStream;
		private Thread ParsingThread;
		private bool pIsInitialized;
		private ExecutionState pState = ExecutionState.Stopped;
		private string pThreadName;
		private ManualResetEvent ParsingWaitHandle = new ManualResetEvent(false);
		private ManualResetEvent PausingWaitHandle = new ManualResetEvent(false);
		private ManualResetEvent ParsingIterationCompleteWaitHandle = new ManualResetEvent(false);
		private bool pIsRecording;
		private Stream pRecordingStream;
		private Packet pLastPacketReceived;
		private Packet pLastPacketSent;
		private TimeSpan pReadTimeout = TimeSpan.FromSeconds(5);
		private TimeSpan pWriteTimeout = TimeSpan.FromSeconds(5);
        private object RecordingSyncRoot = new object();
        private int _MaximumReconnectionAttempts = -1;
        private int _CurrentReconnectionAttempts;
#if PocketPC && !Framework20
        /* Compact Framework 1.0 requires special handling to ensure that the
         * parsing thread is started and stopped gracefully.
         */
        private ManualResetEvent ShutdownCompleteWaitHandle = new ManualResetEvent(true);
		private bool pIsParsingThreadAlive;
#endif
#if PocketPC || !Framework20
		// Controls the priority of the interpreter's parsing thread
        private ThreadPriority pThreadPriority;
#endif
#if !Framework20 || PocketPC
        private static IntPtr EmptyIntPtr = new IntPtr(-1);
#endif

#if Smartphone && !DesignTime
        // The priority of the Stop thread is above normal to ensure that the interpreter is
        // properly shut down.  This is a very sensitive setting (AboveNormal), so avoid
        // changing it.
		private ThreadPriority pStopThreadPriority = ThreadPriority.AboveNormal;
#endif

#if PocketPC && !Framework20 && !Smartphone
        // In Compact Framework 1.0, we cannot use static finalizers, so we must have a
        // PowerEvent object for each instance of the Interpreter.  In CF2, the PowerEvent
        // is static (and can be finalized safely).
		private PowerEvent PowerMonitor;
#endif

        // Used to ensure that the interpreter changes state once at a time
        private object StateChangeSyncRoot = new object();

#if !PocketPC || DesignTime
        // We'll allow half a second to shut down on desktop platforms
		private const int MaximumGracefulShutdownTime = 500;
#elif PocketPC && Framework20
        // We'll allow half a second to shut down on CF2 platforms
        private const int MaximumGracefulShutdownTime = 500;
#endif

        #endregion

        #region Events

        public event CancelableEventHandler Connecting;
		public event EventHandler Connected;
		public event EventHandler Disconnecting;
		public event EventHandler Disconnected;
		/// <summary>Occurs when the underlying stream stops providing raw data.</summary>
		public event ExceptionEventHandler TimeoutOccurred;
		public event ExceptionEventHandler ExceptionOccurred;
#if PocketPC
		public event EventHandler DeviceSuspended;
		public event EventHandler DeviceResumed;
#endif

		/// <summary>Occurs when the next available packet of GPS data should be parsed.</summary>
		/// <remarks>Must be overridden.  This method is responsible for
		/// reading and interpreting data from the raw data stream.  This 
		/// method is called within a multithreaded loop once Start method is
		/// called until the Stop method is called.
		/// </remarks>
		protected abstract void OnParseIncomingData();

		/// <summary>Occurs when a packet of data has been received from the underlying stream.</summary>
		/// <remarks>If recording is enabled, the packet will be saved to the
		/// recording stream.</remarks>
		protected virtual void OnPacketReceived(Packet packet)
		{
			// Remember the last received packet
			SetLastPacketReceived(packet);
			// Are we logging data?
			if (pIsRecording)
			{
				// Yes.  Get the sentence in bytes
				byte[] Buffer = packet.GetBytes();
                // Lock the recording stream
                lock (RecordingSyncRoot)
                {
                    pRecordingStream.Write(Buffer, 0, Buffer.Length);
                }
				// And notify of the written record
				OnPacketRecorded(packet);
			}
		}

		/// <summary>
		/// Occurs when a packet of data has been sent to the GPS device.
		/// </summary>
		/// <param name="packet">A <strong>Packet</strong> or derived class
		/// containing a single packet of GPS data.</param>
		/// <remarks>If recording is enabled, the packet will be saved to the
		/// recording stream.</remarks>
		protected virtual void OnPacketSent(Packet packet)
		{
			// Remember the last sent packet
			SetLastPacketSent(packet);
			// Are we logging data?
			if (pIsRecording)
			{
				// Yes.  Write the sentence
				byte[] Buffer = packet.GetBytes();
				pRecordingStream.Write(Buffer, 0, Buffer.Length);
				// And notify of the written record
				OnPacketRecorded(packet);
			}
		}

		/// <summary>Occurs when a packet of data has been saved to the recording stream.</summary>
		protected virtual void OnPacketRecorded(Packet packet)
		{
		}

		/// <summary>Occurs when an invalid or malformed block of data is received.</summary>
		protected virtual void OnExceptionOccurred(Exception exception)
		{
			if (ExceptionOccurred != null)
#if PocketPC && !Framework20
				ExceptionOccurred(this, new ExceptionEventArgs(exception));
#else
                DynamicEvent.Invoke(ExceptionOccurred, this, new ExceptionEventArgs(exception));
#endif
		}

		/// <summary>Occurs when data has not arrived within the expected period of time.</summary>
		protected virtual void OnTimeoutOccurred(Exception exception)
		{
			if (TimeoutOccurred != null)
#if PocketPC && !Framework20
				TimeoutOccurred(this, new ExceptionEventArgs(exception));
#else
                DynamicEvent.Invoke(TimeoutOccurred, this, new ExceptionEventArgs(exception));
#endif
		}

		#endregion

		#region Constructor / Destructor


		protected Interpreter(string threadName, Stream input)
			: this(threadName)
		{
			// Remember the base stream
			BaseStream = input;
		}

		/// <summary>Creates a new instance.</summary>
		protected Interpreter(string threadName)
			: this()
		{
			// Remember the name of the interpreter
			pThreadName = threadName;
		}

        /// <summary>
        /// Sets the name of the interpreter's thread.
        /// </summary>
        /// <param name="threadName">A <strong>String</strong> describing the purpose of the thread.</param>
        protected void SetThreadName(string threadName)
        {
            pThreadName = threadName;
        }

		private Interpreter()
		{
			// Register the interpreter type with device detection
			Devices.RegisterInterpreter(this.GetType());

#if PocketPC
            // Set the thread priority depending on the platform
            switch (Platform.HostPlatformID)
            {
                case HostPlatformID.Smartphone:
                    // On the Smartphone platform, we'll need slightly lower
                    // priority.
                    pThreadPriority = ThreadPriority.BelowNormal;
                    break;
                case HostPlatformID.PocketPC:
                    // On the PocketPC platform, normal priority is okay.
                    pThreadPriority = ThreadPriority.Normal;
                    break;
                case HostPlatformID.WindowsCE:
                    // On the WindowsCE platform, normal priority is okay.
                    pThreadPriority = ThreadPriority.Normal;
                    break;
                case HostPlatformID.Desktop:
                    // On the desktop platform, normal priority is REQUIRED.
                    pThreadPriority = ThreadPriority.Normal;
                    break;
                default:
                    // On unknown platforms, default to Normal priority
                    pThreadPriority = ThreadPriority.Normal;
                    break;                    
            }
#endif

			// Reset all variables
			Reset();
		}

		/// <summary>
		/// Occurs if the interpreter is shut down without closing open resources.
		/// </summary>
		/// <remarks>This finalizer is only called if the interpreter is terminated
		/// while parsing and/or recording streams are open.  The finalizer is
		/// suppressed if no streams are open upon termination.</remarks>
		~Interpreter()
		{
			Dispose(true);
		}

#if PocketPC && !Framework20
		public
#if DesignTime
			new
#endif		
			void Dispose()
		{
			Dispose(true);
		}
#endif

        internal void DisposeWithoutClosingStream()
        {
            try
            {
                if (pState == ExecutionState.Disposed
                    || pState == ExecutionState.Disposing)
                    return;

                // No need to finalize anymore
                GC.SuppressFinalize(this);

                // Signal that we're disposing
                pState = ExecutionState.Disposing;

                Connecting = null;
                Connected = null;
                Disconnecting = null;
                Disconnected = null;
                TimeoutOccurred = null;
                ExceptionOccurred = null;
#if PocketPC
                DeviceSuspended = null;
                DeviceResumed = null;
#endif

                // Start stopping the parsing thread 
                try
                    {
                        Stop(true);
                    }
                    catch
                    {
                    }
                

                //lock (StateChangeSyncRoot)
                {

                    try
                    {
                        // Stop all communications
                        StopRecording();
                    }
                    catch
                    {
                    }

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!PausingWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (PausingWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            PausingWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!ParsingWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (ParsingWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            ParsingWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

#if PocketPC && !Framework20
				// Close the wait handle to free resources
				if (ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
				{
					try
					{
						ShutdownCompleteWaitHandle.Close();
					}
					catch
					{
					}
				}
#endif

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            ParsingIterationCompleteWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

                    // The interpreter is now dead
                    pState = ExecutionState.Disposed;


                }
                Thread.Sleep(0);
            }
#if DEBUG
            catch (Exception ex)
            {
                // Well have to suppress these since this must be clean.
                System.Diagnostics.Debug.WriteLine("Unhandled exception during interpreter shutdown! " + ex.ToString());
            }
#else
				catch
				{
				}
#endif
            finally
            {
                base.Dispose(true);
            }
        }

		/// <summary> 
		/// Shuts down the interpreter and any resources it is using.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
            try
            {
                if (pState == ExecutionState.Disposed
                    || pState == ExecutionState.Disposing)
                    return;

#if !Smartphone
                // No need to finalize anymore
                GC.SuppressFinalize(this);
#endif

                /* At this point, objects can be terminated */
                bool IsAlreadyStopped = (pState == ExecutionState.Stopped);

                // Signal that we're disposing
                pState = ExecutionState.Disposing;

                Connecting = null;
                Connected = null;
                Disconnecting = null;
                Disconnected = null;
                TimeoutOccurred = null;
                ExceptionOccurred = null;
#if PocketPC
                DeviceSuspended = null;
                DeviceResumed = null;
#endif

                // Start stopping the parsing thread 
                if (!IsAlreadyStopped)
                {
                    try
                    {
                        Stop();
                    }
                    catch
                    {
                    }
                }

                //lock (StateChangeSyncRoot)
                {

                    try
                    {
                        // Stop all communications
                        StopRecording();
                    }
                    catch
                    {
                    }

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!PausingWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (PausingWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            PausingWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!ParsingWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (ParsingWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            ParsingWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

#if PocketPC && !Framework20
				// Close the wait handle to free resources
				if (ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
				{
					try
					{
						ShutdownCompleteWaitHandle.Close();
					}
					catch
					{
					}
				}
#endif

                    // Close the wait handle to free resources
#if Framework20 && !PocketPC
                    if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsClosed)
#else
                    if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                    {
                        try
                        {
                            ParsingIterationCompleteWaitHandle.Close();
                        }
                        catch
                        {
                        }
                    }

                    // The interpreter is now dead
                    pState = ExecutionState.Disposed;


                }
                Thread.Sleep(0);
            }
#if DEBUG
			catch(Exception ex)
            {
                // Well have to suppress these since this must be clean.
                System.Diagnostics.Debug.WriteLine("Unhandled exception during interpreter shutdown! " + ex.ToString());
            }
#else
				catch
				{
				}
#endif
			finally
            {
                base.Dispose(disposing);
            }
		}

		#endregion

        /// <summary>
        /// Controls the number of reconnection attempts allowed when the GPS device stops responding.
        /// </summary>
        /// <remarks>This property is used to control how many times the interpreter is allowed to automatically recover a connection.
        /// A value of negative one (-1) will instruct the interpreter to reconnect indefinitely unless the Stop method is called.
        /// A value of zero will instruct the interpreter to never try to reconnect.</remarks>
        public int MaximumReconnectionAttempts
        {
            get
            {
                return _MaximumReconnectionAttempts;
            }
            set
            {
                if (value < -1)
                    throw new ArgumentOutOfRangeException("The number of maximum reconnection attempts must be -1 or greater.  A value of -1 indicates infinite reconnection attempts and 0 indicates no reconnection attempt.");

                // Set the new maximum reconnection value
                _MaximumReconnectionAttempts = value;
            }
        }

		protected virtual void OnConnecting(CancelableEventArgs e)
		{
			// Signal an event
			try
			{
#if DEBUG
                //System.Diagnostics.Debug.WriteLine("Connecting...");                 
#endif

				if (Connecting != null)
#if PocketPC && !Framework20
					Connecting(this, e);
#else
                    DynamicEvent.Invoke(Connecting, this, e);
#endif
			}
			catch
			{
			}
		}

		protected virtual void OnConnected()
		{
            try
            {

#if DEBUG
                //System.Diagnostics.Debug.WriteLine("Connected.");
#endif


			if (Connected != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginConnected));
#else
                    DynamicEvent.BeginInvoke(Connected, this, EventArgs.Empty);
#endif
            }
            catch
            {
            }
		}

#if PocketPC && !Framework20
		private void BeginConnected(object state)
		{
			try
			{
				if(Connected != null)
					Connected(this, EventArgs.Empty);           
			}
			catch
			{
			}
		}
#endif

        /// <summary>
        /// Occurs when a connection to a GPS data source is about to be closed.
        /// </summary>
		protected virtual void OnDisconnecting()
		{
#if DEBUG
            //System.Diagnostics.Debug.WriteLine("Disconnecting...");
#endif

            try
            {
                if (Disconnecting != null)
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginDisconnecting));
#else
                    DynamicEvent.BeginInvoke(Disconnecting, this, EventArgs.Empty);
#endif
            }
            catch
            {
            }
		}

#if PocketPC && !Framework20
		private void BeginDisconnecting(object state)
		{
			try
			{
				if(Disconnecting != null)
					Disconnecting(this, EventArgs.Empty);           
			}
			catch
			{
			}        
		}
#endif

        /// <summary>
        /// Occurs when an open connection to a GPS data source has been closed.
        /// </summary>
		protected virtual void OnDisconnected()
		{
            // Reset local variables
            Reset();

            // And signal of the disconnection
            if (Disconnected != null && pState == ExecutionState.Stopped)
            {
#if PocketPC && !Framework20
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginDisconnected));
#else
                DynamicEvent.BeginInvoke(Disconnected, this, EventArgs.Empty);
#endif
#if DEBUG
                //System.Diagnostics.Debug.WriteLine("Disconnected.");
#endif
            }
#if DEBUG
			else
            {
                //System.Diagnostics.Debug.WriteLine("No Disconnected notification!");
            }
#endif

		}

#if PocketPC && !Framework20
		private void BeginDisconnected(object state)
		{
			try
			{
				if(Disconnected != null)
					Disconnected(this, EventArgs.Empty);           
			}
			catch
			{
			}        
		}
#endif

		#region Asynchronous Starting
#if !PocketPC
		private delegate void StartDelegate();
		private StartDelegate StartDelegateInstance;

		/// <summary>Starts the interpreter asynchronously.</summary>
		public IAsyncResult BeginStart(AsyncCallback callback, object state)
		{
			// Start asynchronously
			StartDelegateInstance = new StartDelegate(Start);
			return StartDelegateInstance.BeginInvoke(callback, state);
		}

		/// <summary>Completes an asynchronous start command.</summary>
		public void EndStart(IAsyncResult asyncResult)
		{
			StartDelegateInstance.EndInvoke(asyncResult);
		}
#endif
		#endregion

		/// <summary>
		/// Controls the stream responsible for reading raw data from the underlying stream.
		/// </summary>
#if !PocketPC || DesignTime
		[Category("Data")]
		[Description("Controls the stream responsible for reading raw data from the underlying stream.")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
		public virtual Stream BaseStream
		{
			get
			{
				return pBaseStream;
			}
			set
			{
				// If we're disposed, forbid changing stream
				if (pState == ExecutionState.Disposed)
					throw new ObjectDisposedException(pBaseStream == null ? "Null" : pBaseStream.GetType().ToString(), "The base stream for the interpreter cannot be changed because the Interpreter has been disposed.");

				// Set the new value
                lock(StateChangeSyncRoot)
                {
                    // Close the existing stream
                    if (pBaseStream != null)
                        pBaseStream.Close();
                    
                    pBaseStream = value;

                    // And notify the interpreter so it can reassign its own stream
                    OnBaseStreamChanged(pBaseStream);
                }
			}
		}

#if PocketPC || !Framework20
		/// <summary>
		/// Controls the priority of GPS data processing relative to other running threads.
		/// </summary>
		/// <remarks><para>This is an feature meant for advanced developers only.  Changing this property
		/// without a thorough understanding of other threads running in the application can lead
		/// to unpredictable results such as application unresponsiveness.</para>
		/// <para>This property allows control over the thread used to process incoming GPS data.  Typically,
		/// this property is set to BelowNormal so that the foreground application has more CPU time.  A
		/// higher value may be preferable for applications which require highly-sensitive timing, whereas
		/// a lower value can be preferable if the foreground application is already demanding on the processor.</para></remarks>
#if DesignTime
		[Category("Behavior")]
		[Description("Controls the priority of GPS data processing relative to other running threads.")]
#endif
#if !PocketPC && Framework20
		[Obsolete("Changing thread priorty on Desktop Framework 2.0 can cause instability.  A setting of Normal is recommended for all threads.")]
#endif
		public ThreadPriority ThreadPriority
		{
			get
			{
				return pThreadPriority;
			}
			set
			{
				pThreadPriority = value;
			}
		}

        //public ThreadPriority StopThreadPriority
        //{
        //    get
        //    {
        //        return pStopThreadPriority;
        //    }
        //    set
        //    {
        //        pStopThreadPriority = value;
        //    }
        //}
#endif

		/// <summary>Indicates the current execution state of the interpreter.</summary>
#if !PocketPC || DesignTime
		[Category("Behavior")]
		[Description("Indicates the current execution state of the interpreter.")]
#endif
		public ExecutionState State
		{
			get
			{
				return pState;
			}
		}

		/// <summary>Occurs when the source of GPS data has changed.</summary>
		protected virtual void OnBaseStreamChanged(Stream baseStream)
		{}

		/// <summary>Indicates the name of the protocol handled by the interpreter.</summary>
		/// <remarks>This property returns the full name of the protocol it handles.  For example,
		/// if the interpreter handles the NMEA protocol, it would return "NMEA" or "NMEA-0183".</remarks>
#if !PocketPC || DesignTime
		[Category("Data")]
		[Description("Indicates the name of the protocol handled by the interpreter.")]
#endif
		public abstract string Protocol { get; }

		/// <summary>
		/// Controls the maximum amount of time allowed for read operations to complete successfully.
		/// </summary>
#if !PocketPC || DesignTime
		[Category("Behavior")]
		[Description("Controls the maximum amount of time allowed for read operations to complete successfully.")]
#endif
        public virtual TimeSpan ReadTimeout
		{
			get
			{
				return pReadTimeout;
			}
			set
			{
				if(pReadTimeout.Equals(value))
					return;
				pReadTimeout = value;
			}
		}

		/// <summary>
		/// Controls the maximum amount of time allowed for write operations to complete successfully.
		/// </summary>
#if !PocketPC || DesignTime
        [Category("Behavior")]
        [Description("Controls the maximum amount of time allowed for write operations to complete successfully.")]
#endif
		public virtual TimeSpan WriteTimeout
		{
			get
			{
				return pWriteTimeout;
			}
			set
			{
				if(pWriteTimeout.Equals(value))
					return;
				pWriteTimeout = value;
			}
		}

		/// <summary>Indicates if data being sent to and from the underlying stream is being recorded.</summary>
#if !PocketPC || DesignTime
		[Category("Data")]
		[Description("Indicates if data being sent to and from the underlying stream is being recorded.")]
#endif
		public virtual bool IsRecording
		{
			get
			{
				return pIsRecording;
			}
		}

		/// <summary>Indicates if the specified data can be recognized by the interpreter.</summary>
		public abstract bool CanParse(Packet packet);
		/// <summary>Interprets a single block of data from the underlying stream.</summary>
		public abstract void Parse(Packet packet);

		/// <summary>
		/// Returns a new Stream ready to receive GPS data.
		/// </summary>
		/// <returns>A <strong>Stream</strong> object.</returns>
		/// <remarks>When an interpreter must be restarted, a new underlying stream is
		/// required.  Since Stream objects cannot be reopened once they are closed,
		/// a new instance of a stream must be provided to the interpreter in order for
		/// a restart to succeed.  This method attempts to create and configure a new
		/// stream during a restart.  This method can successfully recreate GpsStream,
		/// SerialStream and BluetoothStream objects, but it cannot recreate any other
		/// kind of stream.</remarks>
#if !PocketPC
        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
#endif
		private Stream GetFreshStream()
		{
			// Assign a new base stream
			if (pBaseStream == null)
			{
				// Make a new GPS stream
				return new GpsStream(Protocol);
			}
			else if (pBaseStream is GpsStream)
			{
				// Get the current stream
				GeoFramework.IO.GpsStream CurrentInstance =
					(GeoFramework.IO.GpsStream)pBaseStream;
				// And set the base stream to a fresh instance
				return CurrentInstance.Clone();
			}
			else if (pBaseStream is GeoFramework.IO.Serial.SerialStream)
			{
				// Get the current stream
				GeoFramework.IO.Serial.SerialStream CurrentInstance =
					(GeoFramework.IO.Serial.SerialStream)pBaseStream;
				// And set the base stream to a fresh instance
				return CurrentInstance.Clone();
			}
			else if (pBaseStream is GeoFramework.IO.Bluetooth.BluetoothStream)
			{
				// Get the current stream
				GeoFramework.IO.Bluetooth.BluetoothStream CurrentInstance =
					(GeoFramework.IO.Bluetooth.BluetoothStream)pBaseStream;
				// And set the base stream to a fresh instance
				return CurrentInstance.Clone();
			}
			else if (pBaseStream is Emulators.Emulator)
			{
				// Get the current stream
				Emulators.Emulator CurrentInstance =
					(Emulators.Emulator)pBaseStream;
				// And set the base stream to a fresh instance
				return CurrentInstance.Clone();
			}
			else
			{
				throw new InvalidOperationException("The current " + pBaseStream.GetType().ToString() + " providing data to the interpreter was not recognized and therefore could not be cloned.  Please restart the interpreter with a new Stream manually, or provide a GpsStream.");
			}
		}

		/// <summary>
		/// Closes the underlying stream and attempts to replace it with a new
		/// stream of the same type.
		/// </summary>
        public void Restart()
        {
            lock (StateChangeSyncRoot)
            {

                if (pState == ExecutionState.Restarting
                    || pState == ExecutionState.Resuming 
                    || pState == ExecutionState.Starting)
                {
                    // Wait a bit for things to get running again
                    //Thread.Sleep(1000);
                }
                else if (pState == ExecutionState.Running)
                {
                    // Careful, we might be recovering from power loss.                        
                    //Thread.Sleep(500);
                    pState = ExecutionState.Resuming;

                    // Loop until a resume succeeds
                    while (pState == ExecutionState.Resuming)
                    {
                        // Signal that a shutdown is imminent
                        OnDisconnecting();

                        try
                        {
                            // Close the connection immediately!
                            if (pBaseStream != null)
                                pBaseStream.Close();

                        }
                        catch
                        {
                        }
                        finally
                        {
                            // Signal that we're disconnected
                            OnDisconnected();
                        }

                        try
                        {

                            // Signal that we're connecting
                            CancelableEventArgs args = new CancelableEventArgs(false);
                            OnConnecting(args);
                            if (args.Canceled)
                            {
                                throw new InvalidOperationException("The attempt to connect to the GPS device has been cancelled during the Connecting event.");
                            }
                        }
                        catch 
                        {
                            // Signal that a shutdown is imminent
                            OnDisconnecting();
                            OnDisconnected();
                            throw;
                        }

                        try
                        {
                            // Make a new stream
                            BaseStream = GetFreshStream();

                            // Force it to open
                            long Len = pBaseStream.Length;
                        }
                        catch
                        {
                            // Connection failed
                            OnDisconnecting();
                            OnDisconnected();
                            throw;
                        }

                        // Signal that we're connected now
                        OnConnected();

                        Application.DoEvents();
                        Thread.Sleep(1000);

                        // And signal that we're running again
                        pState = ExecutionState.Running;
                        //}
                        //catch
                        //{
                        //    //// Signal that a shutdown is imminent
                        //    //OnDisconnecting();

                        //    //// Close the connection immediately!
                        //    //if (pBaseStream != null)
                        //    //    pBaseStream.Close();

                        //    //// Signal that we're disconnected
                        //    //OnDisconnected();

                        //    //Application.DoEvents();
                        //    //Thread.Sleep(1000);

                        //    // Signal that we're still resuming
                        //    pState = ExecutionState.Resuming;
                        //}
                        //finally
                        //{
                        //    Thread.Sleep(0);
                        //}
                    }

                    //Thread.Sleep(0);

                }                
                else if (pState == ExecutionState.Stopped)
                {

                    // Create a "fresh" base stream with the same settings
                    BaseStream = GetFreshStream();

                    //Thread.Sleep(0);

                    // And start again
                    Start();
                }
                else if (pState == ExecutionState.DeviceSuspending)
                {
                    // A restart is already pending, so just exit
                }
                else if (pState == ExecutionState.Disposing
                    || pState == ExecutionState.Disposed)
                {
                    throw new ObjectDisposedException(pBaseStream == null ? "Null" : pBaseStream.GetType().ToString(), "The interpreter was been shut down as a result of calling the Dispose method.  Please create a new instance and call the Restart method again.");
                }
                else if (pState == ExecutionState.DeviceResuming)
                {
                    // Just wait for the existing restart to complete.
                    //Thread.Sleep(2000);
                }
                else
                {
                    throw new InvalidOperationException("The interpreter is " + pState + ", which is an unhandled state to process a restart request.");
                }
            }
			Thread.Sleep(0);
        }

		/// <summary>Begins receiving and interpreting information from the underlying stream.</summary>
		public void Start()
		{
			// If it's already started, do nothing
			if (pState != ExecutionState.Stopped)
				return;

			if (pState == ExecutionState.Disposing
				|| pState == ExecutionState.Disposed)
				throw new ObjectDisposedException(pBaseStream == null ? "Null" : pBaseStream.GetType().ToString(), "The interpreter was been shut down as a result of calling the Dispose method.  Please create a new instance and call the Start method again.");

			// Reset variables
			if (!pIsInitialized)
			{
				try
				{
					Reset();
					pIsInitialized = true;
				}
				catch
				{
					throw;
				}
			}

			// Block any state change operations until this completes
			lock (StateChangeSyncRoot)
			{
				// Flag that we're starting the interpreter
				pState = ExecutionState.Starting;

#if PocketPC && !Framework20
				// First, wait for any previous shutdown attempt to complete
				ShutdownCompleteWaitHandle.WaitOne();
#endif

				try
				{
					// Automatically locate a GPS device if no underlying stream is present, or if it's
					// already set to a "GpsStream" object (which may be closed)
					if (pBaseStream == null)
						BaseStream = new GpsStream(Protocol);
					else
					{
						// If the base stream is automatic, make it to a fresh one
                        GpsStream AutomaticStream = pBaseStream as GpsStream;
                        if (AutomaticStream != null && AutomaticStream.IsClosed)
						{
					        BaseStream = new GpsStream();
						}
					}

					try
					{
						CancelableEventArgs e = new CancelableEventArgs(false);
						OnConnecting(e);
						// Was the connection cancelled?
						if(e.Canceled)						
							throw new InvalidOperationException("An attempt to start the interpreter was cancelled before it could begin.");

                        // Force the stream to open by checking its length
						long StreamOpened = pBaseStream.Length;
                    }
					catch
					{
                        OnDisconnecting();
                        OnDisconnected();
						throw;
					}

					// Signal that we're connected
					OnConnected();

                    try
                    {
                        // Configure a new thread for interpretation
                        ParsingThread = new Thread(new ThreadStart(ParsingThreadProc));

#if Framework20 && !PocketPC
                        // Priority is not set on DF2.0 due to stability issues
#else
						// Set the priority on mobile devices
						if(Platform.HostPlatformID != HostPlatformID.Desktop)
							ParsingThread.Priority = pThreadPriority;
#endif

#if !PocketPC || Framework20
                        ParsingThread.Name = pThreadName;
                        ParsingThread.IsBackground = true;
#endif
                        // If the interpreter is disposed, exit immediately
                        if (pState == ExecutionState.Disposed)
                        {
                            OnDisconnecting();
                            OnDisconnected();
                            return;
                        }

                        // Make a new event for pausing, defaulted to SIGNALED
#if Framework20 && !PocketPC
                        if (!PausingWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (PausingWaitHandle.Handle != EmptyIntPtr)
#endif
                            PausingWaitHandle.Set();

                        // Start the new thread and wait for it to rev up
                        ParsingWaitHandle.Reset();

                        ParsingThread.Start();

                        // Let it start up
                        Thread.Sleep(0);

                        // Flag that the interpreter is now running
                        //pState = ExecutionState.Running;
                    }
                    catch
                    {
                        OnDisconnecting();
                        OnDisconnected();
                        throw;
                    }
				}
				catch(Exception ex)
				{

					// Signal that we're shutting down
                    //if (State == ExecutionState.Running)
                    {
                        //OnDisconnecting();

                        // That startup attempt failed.  Close the base stream
                        if (pBaseStream != null)
                            pBaseStream.Close();

                        // We're no longer connected
                        //OnDisconnected();
                    }

					// Flag that the interpreter is stopped
					pState = ExecutionState.Stopped;
					throw ex;
				}
			}
			Thread.Sleep(0);
		}

		#region Parsing Thread

		/// <summary>Stores the most recently-received packet.</summary>
		/// <remarks>This property does nothing by default, but when overridden
		/// it stores the most recently-received packet.  This property is typically
		/// used during exception handling to determine if the exception was caused
		/// by malformed data.</remarks>
#if !PocketPC || DesignTime
		[Category("Data")]
		[Description("Stores the most recently-received packet.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
		public virtual Packet LastPacketReceived
		{
			get
			{
				return pLastPacketReceived;
			}
		}

        protected virtual void SetLastPacketReceived(Packet packet)
        {
            pLastPacketReceived = packet;
        }

		/// <summary>Stores the most recently-sent packet.</summary>
		/// <remarks>This property does nothing by default, but when overridden
		/// it stores the most recently-sent packet.  This property is typically
		/// used during exception handling to determine if the exception was caused
		/// by malformed data.</remarks>
#if !PocketPC
		[Category("Data")]
		[Description("Stores the most recently-sent packet.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
		public virtual Packet LastPacketSent
		{
			get
			{
				return pLastPacketSent;
			}
		}

        protected virtual void SetLastPacketSent(Packet packet)
        {
            pLastPacketSent = packet;
        }
		
		/// <returns>A Boolean, True if the interpreter can interpret data arriving from the device.</returns>
		/// <remarks>Automatic device detection works by analyzing the raw data received from
		/// devices on the local machine.  GPS interpreters perform the brunt of the work by
		/// reading raw data from the device, then signaling if data is recognized.  In order to
		/// deliver better performance, this method is called on a separate thread.  As a result,
		/// classes inheriting from this class should declare all variables locally in order to
		/// avoid thread synchronization problems.</remarks>
		/// <summary>Reads raw data from the device in an attempt to recognize it as GPS data.</summary>
#if !PocketPC
        [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
#endif
        public abstract DeviceRecognitionResult IsDeviceRecognizable(Device device);

		private void ParsingThreadProc()
		{
#if PocketPC && !Smartphone 
	#if Framework20
            // Hook that biatch in
            try
            {
                PowerEvent.DeviceSuspended += new EventHandler(PowerEvent_DeviceSuspended);
                PowerEvent.DeviceResumed += new EventHandler(PowerEvent_DeviceResumed);
            }
            catch
            {
            }
	#elif !DesignTime
			try
			{
				if(PowerMonitor == null)
					PowerMonitor = new PowerEvent();
			}
			catch
			{
			}
	#endif
#endif

            try
            {
                lock (StateChangeSyncRoot)
                {
                    // Is we're already disposed, exit immediately
                    if (pState == ExecutionState.Disposed)
                        return;

#if PocketPC && !Framework20
                    // Flag that we're alive
                    pIsParsingThreadAlive = true;
#endif

                    // Set the reset event to signal we're alive
                    ParsingWaitHandle.Set();

                    // Flag that the interpreter is now running
                    pState = ExecutionState.Running;
                }
				Thread.Sleep(0);

                // Reset the connection retry count
                _CurrentReconnectionAttempts = 0;

                // Loop until processing is no longer active
                while (pState == ExecutionState.Starting
                    || pState == ExecutionState.Running
                    || pState == ExecutionState.Pausing
                    || pState == ExecutionState.Paused
                    || pState == ExecutionState.Restarting
                    || pState == ExecutionState.Resuming
                    || pState == ExecutionState.DeviceSuspending
                    || pState == ExecutionState.DeviceResuming)
                {
                    try
                    {
                        // If we're restarting, wait for a moment.  Waiting allows the connection
                        // to be reopened before attempting to read data
                        if (pState == ExecutionState.Restarting
                            || pState == ExecutionState.Starting
                            || pState == ExecutionState.Pausing
                            || pState == ExecutionState.DeviceSuspending
                            || pState == ExecutionState.DeviceResuming)
                        {
                            // Wait for a change in state
                            lock (StateChangeSyncRoot)
                            {
#if (PocketPC || !Framework20)
                                // Change priority if necessary
#if !Framework10
                                if(Environment.OSVersion.Platform == PlatformID.WinCE)
#endif
                                    ParsingThread.Priority = ThreadPriority.BelowNormal;
#endif

                                Thread.Sleep(500);
                            }
							Thread.Sleep(0);
                            continue;
                        }
#if Framework20 && !PocketPC
                        // Priority is not set on DF2.0 due to stability issues
#else
                        else
                        {
							// Set the priority on mobile devices only
							if(Platform.HostPlatformID != HostPlatformID.Desktop)
								ParsingThread.Priority = pThreadPriority;
						}
#endif

                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Reset();
                            }
                            catch
                            {
                            }
                        }

                        // Parse any available
                        if(pState == ExecutionState.Running)
                            OnParseIncomingData();

                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }

                        // The underlying stream has been closed!  Are we restarting?
                        if (pState == ExecutionState.Restarting)
                        {
                            // Yes.  Ignore this error because we'll be up and running again shortly
                            continue;
                        }
                        else if (pState == ExecutionState.Stopping)
                        {
                            // the interpreter was stopped!  Exit immediately
                            break;
                        }
                        else if (pState == ExecutionState.Running)
                        {
                            Restart();
                            continue;
                        }
                        //else
                        //{
                        //    //MessageBox.Show("Object disposed!  Unknown state.");
                        //}
                    }
                    catch (TimeoutException ex)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }

                        // The underlying stream has been closed!  Are we restarting?
                        if (pState == ExecutionState.Running)
                        {
                            // Careful, we might be recovering from power loss.                        
                            //Thread.Sleep(500);

                            lock (StateChangeSyncRoot)
                            {
                                // Are we still running?  If so, continue
                                if (pState != ExecutionState.Running)
                                    continue;

                                // Signal that we're resuming the interpreter
                                pState = ExecutionState.Resuming;

								// Raise a timeout event
								OnTimeoutOccurred(ex);

                                // Signal that a shutdown is imminent
                                OnDisconnecting();

                                try
                                {
                                    // Close the connection immediately!
                                    if (pBaseStream != null)
                                        pBaseStream.Close();
                                }
                                catch
                                {
                                }
                                finally
                                {
                                    OnDisconnected();
                                }

                                // Are we managing reconnection attempts?
                                switch (_MaximumReconnectionAttempts)
                                {
                                    case -1:
                                        // Reconnection attempts will always be allowed.
                                        break;
                                    case 0:
                                        // Reconnection attempts will never be allowed.
                                        pState = ExecutionState.Stopped;

                                        // Reset the connection count
                                        _CurrentReconnectionAttempts = 0;
                                        return;
                                    default:
                                        // Increase the connection attempt count
                                        _CurrentReconnectionAttempts++;

                                        // If the # of connections exceeds the maximum, abort
                                        if (_CurrentReconnectionAttempts > _MaximumReconnectionAttempts)
                                        {
                                            // Cancel connecting and stop completely
                                            pState = ExecutionState.Stopped;

                                            // Reset the connection count
                                            _CurrentReconnectionAttempts = 0;
                                            return;
                                        }
                                        break;
                                }

                                try
                                {
                                    // Signal that we're connecting
                                    CancelableEventArgs args = new CancelableEventArgs(false);
                                    OnConnecting(args);
                                    if (args.Canceled)
                                    {
                                        throw new InvalidOperationException("The attempt to connect to the GPS device has been cancelled during the Connecting event.");
                                    }
                                }
                                catch(InvalidOperationException)
                                {
                                    // Cancel connecting and stop completely
                                    pState = ExecutionState.Stopped;
                                    OnDisconnecting();
                                    OnDisconnected();
                                    return;
                                }

                                try
                                {
                                    // Make a new stream
                                    BaseStream = GetFreshStream();

                                    // Force it to open
                                    long Len = pBaseStream.Length;
                                }
                                catch
                                {
                                    // Connection failed
                                    OnDisconnecting();
                                    OnDisconnected();

                                    // Try to connect again
                                    pState = ExecutionState.Running;
                                    Thread.Sleep(0);
                                    continue;
                                }

                                // Signal that we're connecting
                                OnConnected();

                                // And signal that we're running again
                                pState = ExecutionState.Running;
                            }
                            Thread.Sleep(0);

                            //MessageBox.Show("Restart completed.");
                        }
                        else if (pState == ExecutionState.Stopping)
                        {
                            // We're shutting down, so exit
                            break;
                        }
                        else if (pState == ExecutionState.Stopped)
                        {
                            // We're shutting down, so exit
                            break;
                        }
                        else if (pState == ExecutionState.DeviceSuspending)
                        {
                            // The suspension code is still running, so just wait for it
                            continue;
                        }
                        else
                        {
                            // In any other state than Running, just continue
                            throw new InvalidOperationException("Unhandled state for handling TimeoutException: " + pState);
                        }
                    }
#if !PocketPC || Framework20
                    catch (ThreadAbortException)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }
                        // Just quit!
                        break;
                    }
#endif
                    catch (PacketException ex)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }

                        // Pass along the error
                        if (pState == ExecutionState.Running)
                            OnExceptionOccurred(ex);
                    }
                    catch (IOException ex)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }

                        if (pState == ExecutionState.Running)
                        {
                            OnExceptionOccurred(
                                new PacketException(LastPacketReceived, "An error was encountered while attempting to communicate with the GPS device.  It may be busy processing another request.  See inner exception for details.", ex));
                        }
                    }
                    catch (Exception ex)
                    {
                        // Signal that parsing is complete
#if Framework20 && !PocketPC
                        if (!ParsingIterationCompleteWaitHandle.SafeWaitHandle.IsInvalid)
#else
                        if (ParsingIterationCompleteWaitHandle.Handle != EmptyIntPtr)
#endif
                        {
                            try
                            {
                                ParsingIterationCompleteWaitHandle.Set();
                            }
                            catch
                            {
                            }
                        }

                        if (pState == ExecutionState.Running)
                        {
                            OnExceptionOccurred(
                                new PacketException(LastPacketReceived, "An error was encountered while processing raw GPS data.  See inner exception for details.", ex));
                        }
                    }
                    finally
                    {
                        // Let other threads work
                        Thread.Sleep(0);

                        // If we're paused, this will wait indefinitely
#if Framework20 && !PocketPC
                        if (!PausingWaitHandle.SafeWaitHandle.IsInvalid
#else
                        if (PausingWaitHandle.Handle != EmptyIntPtr
#endif
                            && pState != ExecutionState.Disposed
                            && pState != ExecutionState.Disposing)
                        {
                            try
                            {
                                PausingWaitHandle.WaitOne();
                            }
                            catch(ObjectDisposedException)
                            {
                                // It's shut down, so exit
                            }
                        }
                    }
                }
            }
#if !PocketPC || Framework20
            catch (ThreadAbortException)
            {
                // Ignore!
            }
#endif
            catch
            {
                // Ignore.
            }
			finally
			{
				// Flag that we're dead
#if PocketPC && !Framework20
				pIsParsingThreadAlive = false;
#endif

#if Framework20 && !PocketPC
                if (!ParsingWaitHandle.SafeWaitHandle.IsInvalid)
#else
                if (ParsingWaitHandle.Handle != EmptyIntPtr)
#endif
				{
					try
					{
						// And flag that we're dead
						ParsingWaitHandle.Set();
					}
					catch
					{
						// Ignore.
					}
				}

#if PocketPC && !Smartphone
	#if Framework20
                // Unhook from power events
                try
                {
                    PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                    PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                }
                catch
                {
                }
	#elif !DesignTime
				// Shut down the power monitor
				try
				{
					if(PowerMonitor != null)
						PowerMonitor.Dispose();
				}
				catch
				{
				}
	#endif
#endif

#if PocketPC && !Framework20
                // Signal that shutdown has completed
                if (ShutdownCompleteWaitHandle.Handle != EmptyIntPtr && pState != ExecutionState.Disposed)
                {
                    try
                    {
                        ShutdownCompleteWaitHandle.Set();
                    }
                    catch
                    {
                    }
                }
#endif

				// We're no longer trying to stop
				if(pState != ExecutionState.Disposed)
				{
					pState = ExecutionState.Stopped;

                    // And raise an event to notify
                    OnDisconnected();
				}

                // Give other threads a chance to terminate
                //Thread.Sleep(0);


			}
		}

		#endregion


		/// <summary>Temporarily suspends processing of GPS data.</summary>
		/// <exception cref="IOException">The interpreter thread did not respond to either a graceful shutdown request, nor a forceful shutdown via Thread.Abort.  The thread is being allowed to shut down asynchronously.</exception>
		public virtual void Pause()
		{
			// Are we already paused?  If so, exit
			if (pState == ExecutionState.Running)
			{
				lock (StateChangeSyncRoot)
				{
					try
					{
						pState = ExecutionState.Pausing;
						// Clearing this wait handle causes the interpreter
						// to block on .WaitOne();
						PausingWaitHandle.Reset();

						// We're shutting down, so no pause
						pState = ExecutionState.Paused;
					}
					catch (ObjectDisposedException)
					{
						// The pause failed
						pState = ExecutionState.Running;
					}
				}
				Thread.Sleep(0);
			}
		}

		/// <summary>Pauses recording but allows the flow of incoming data to continue.</summary>
		/// <remarks>This method is typically used to temporarily disable recording.
		/// Data is still read from the device and interpreted, but not recorded.</remarks>
		public virtual void PauseRecording()
		{
			if (pRecordingStream == null)
				throw new InvalidOperationException("No stream exists for recording NMEA data.  Use the StartRecording() method before calling this method.");
			pIsRecording = false;
		}

		/// <summary>Sets all GPS-related variables to empty values.</summary>
		public abstract void Reset();

		/// <summary>
		/// Resumes the processing of raw GPS data.
		/// </summary>
		public virtual void Resume()
		{
			// If the thread is dead or we're already closing, skip
			if (pState == ExecutionState.Paused)
			{
				lock (StateChangeSyncRoot)
				{
					try
					{
						pState = ExecutionState.Resuming;

						// Un-pause the interpreter
						PausingWaitHandle.Set();

						// We're no longer paused
						pState = ExecutionState.Running;
					}
					catch (ObjectDisposedException)
					{
						// Unable to signal unpausing.  The interpreter is probably shutting down
						pState = ExecutionState.Paused;
					}
				}
				Thread.Sleep(0);
			}
		}

		/// <summary>Resumes recording of incoming GPS data.</summary>
		/// <remarks>This method is typically used after a previous call to the 
		/// <see cref="PauseRecording"></see> method.</remarks>
		public virtual void ResumeRecording()
		{
			if (pRecordingStream == null)
				throw new InvalidOperationException("No stream exists for recording NMEA data.  Use the StartRecording() method before calling this method.");
			pIsRecording = false;
		}

		/// <summary>
		/// Begins recording all received data to the specified stream.
		/// </summary>
		/// <param name="output">A <strong>Stream</strong> to receive all recorded data.</param>
public virtual void StartRecording(Stream output)
{
    lock (RecordingSyncRoot)
    {
        // If we're already recording, close that stream first
        if (pIsRecording)
            StopRecording();
        // Remember the output stream
        pRecordingStream = output;
        // And flag that we're recording
        pIsRecording = true;
    }
}

#if PocketPC && !Framework20
		private void StopThreadProc()
		{
			// Resume the interpreter if it's paused
			Resume();

			// Prevent any state changes until this completes
			lock (StateChangeSyncRoot)
			{
				// If we're disposed, just exit
				if(pState == ExecutionState.Disposed)
					return;


				try
				{
					// Wait for other threads
					while (pIsParsingThreadAlive)
					{
						Application.DoEvents();
						Thread.Sleep(0);
					}
				}
				catch
				{
				}
				finally
				{


					// We're no longer trying to stop
					pState = ExecutionState.Stopped;

#if PocketPC && !Framework20
					// Signal that shutdown has completed
					if(ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
					{
						try
						{
							ShutdownCompleteWaitHandle.Set();
						}
						catch
						{
						}
					}
#endif

					// And raise an event to notify
					if (Disconnected != null && pState == ExecutionState.Stopped)
#if !PocketPC || Framework20
                        DynamicEvent.BeginInvoke(Disconnected, this, EventArgs.Empty);
#else
						System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginDisconnected));
#endif
				}
			}
			Thread.Sleep(0);
		}
#endif

        /// <summary>Halts processing of GPS data and closes the underlying stream.</summary>
        /// <remarks>This method will stop all processing, then close the
        /// underlying raw data stream.  To stop processing without closing
        /// the data stream, use the <see cref="Pause"></see> method.</remarks>
        public void Stop()
        {
            Stop(false);
        }


		internal void Stop(bool leaveStreamOpen)
		{
			// Are we already stopped?
            if (pState == ExecutionState.Stopped)
            {
                #region The interpreter is already stopped

                // yes.  Nothing to do, so exit
                return;

                #endregion
            }
            else if (pState == ExecutionState.Stopping)
            {
                #region The interpreter is already shutting down

#if PocketPC && !Framework20
				// We're already stopping, so wait for a shutdown
				if(ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
				{
					try
					{
						ShutdownCompleteWaitHandle.WaitOne();
					}
					catch
					{
					}
				}
#endif
                return;

                #endregion
            }
            else if (pState == ExecutionState.Running)
            {
                #region The interpreter is in a fully-operational state

                // Prevent any state changes until this completes
                lock (StateChangeSyncRoot)
                {
                    // Signal that we are now stopping
                    pState = ExecutionState.Stopping;

#if PocketPC && !Framework20
					// Signal that shutdown is now in progress
					ShutdownCompleteWaitHandle.Reset();
#endif

                    // Raise an event that we're disconnecting
                    OnDisconnecting();

#if PocketPC
					// Wait for a moment just in case we're just
					// finishing a Start sequence.  Bluetooth GPS
					// devices will NOT disconnect unless you give
					// them some time to connect completely first.
                    Thread.Sleep(1000);
#endif

                    // And close the underlying stream
                    if (!leaveStreamOpen)
                    {
                        if (ParsingStream != null)
                            ParsingStream.Close();

                        // And get rid of the Base Stream
                        if (pBaseStream != null)
                            pBaseStream.Close();
                    }

#if !PocketPC || DesignTime || (PocketPC && Framework20)
                    /* On Desktop Framework and Compact Framework 2.0, we can
                     * Join and forcefully abort threads.
                     */

                    try
                    {
                        if (ParsingThread != null && !ParsingThread.Join(MaximumGracefulShutdownTime))
                        {
                            // Forcefully abort the thread
                            ParsingThread.Abort();

                            #region Compact Framework 2.0 Cleanup
#if PocketPC && Framework20
                            /* On Compact Framework 2.0, Thread.Abort does not cause
                             * "finally" clauses to execute!  So, we must take extra
                             * precautions to ensure that the thread is gracefully
                             * shut down and all variables reset properly.
                             */

                            try
                            {
                                // Signal that parsing has completed
                                if (ParsingWaitHandle.Handle != EmptyIntPtr && pState != ExecutionState.Disposed)
                                    ParsingWaitHandle.Set();
                            }
                            catch(ObjectDisposedException)
                            { 
                            }

#if !Smartphone
                            // Unhook from power events
                            try
                            {
                                PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                                PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                            }
                            catch
                            {
                            }
#endif
#endif
                            #endregion
                        }
                    }
                    catch
                    {
                    }

                    // If we're still stopping, raise events
                    if (pState == ExecutionState.Stopping)
                    {
                        // Signal that we are now stopping
                        pState = ExecutionState.Stopped;

                        // Signal we're disconnected
                        OnDisconnected();
                    }
#else
                    // Wait up to two seconds for a graceful exit
                    for(int i = 0; i < 20; i++)
                    {
                        // Was it exited?
                        if(!pIsParsingThreadAlive)
                            // Yes. 
                            break;

                        // No.  Keep waiting
                        Thread.Sleep(100);
                        Thread.Sleep(0);
                    }

                    // If it's STILL alive, forcefully exit
                    if (pIsParsingThreadAlive)
                    {
                        // That didn't work.  Forcefully abort the thread
                        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnStop));
                    }        
                    else
                    {
#if PocketPC && !Framework20
						// Signal that shutdown is now in progress
						ShutdownCompleteWaitHandle.Set();
#endif

                        OnDisconnected();
                    }
#endif
                }
                // Let other threads get a chance to lock
                Thread.Sleep(0);

                #endregion
            }
            else if (pState == ExecutionState.Disposing)
            {
                #region The interpreter is in the process of terminating permanently

#if PocketPC && !Framework20
				// Signal that shutdown is now in progress
                try
                {
                    ShutdownCompleteWaitHandle.Reset();
                }
                catch
                {
                }
#endif
                if (!leaveStreamOpen)
                {
                    // And close the underlying stream
                    if (ParsingStream != null)
                        ParsingStream.Close();

                    // And get rid of the Base Stream
                    if (pBaseStream != null)
                        pBaseStream.Close();
                }

#if !PocketPC || DesignTime || (PocketPC && Framework20)
                /* On Desktop Framework and Compact Framework 2.0, we can
                 * Join and forcefully abort threads.
                 */
                try
                {
                    if (ParsingThread != null && !ParsingThread.Join(MaximumGracefulShutdownTime))
                    {
                        // Forcefully abort the thread
                        ParsingThread.Abort();

                        #region Compact Framework 2.0 Cleanup
#if PocketPC && Framework20
                        /* On Compact Framework 2.0, Thread.Abort does not cause
                             * "finally" clauses to execute!  So, we must take extra
                             * precautions to ensure that the thread is gracefully
                             * shut down and all variables reset properly.
                             */

#if !Smartphone
                        // Unhook from power events
                        try
                        {
                            PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                            PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                        }
                        catch
                        {
                        }
#endif
#endif
                        #endregion
                    }
                }
                catch
                {
                }

#if PocketPC && !Framework20
                    // Signal that shutdown is now finished
                    if (ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
                    {
                        try
                        {
                            ShutdownCompleteWaitHandle.Set();
                        }
                        catch
                        {
                        }
                    }
#endif
#else
                /* Compact Framework 1.0... since we're disposing, just let the thread exit on its own.
				 * There's really nothing else we can do since no forceful abort is possible.
				 * Sigh. */			
#endif
                #endregion
            }
            else if (pState == ExecutionState.Starting)
            {
                #region The interpreter is in the process of starting

                // Prevent any state changes until this completes
                lock (StateChangeSyncRoot)
                {
                    // Signal that we are now stopping
                    pState = ExecutionState.Stopping;

#if PocketPC && !Framework20
					// Signal that shutdown is now in progress
					ShutdownCompleteWaitHandle.Reset();
#endif

                    // Raise an event that we're disconnecting
                    OnDisconnecting();

#if PocketPC
					// Wait for a moment just in case we're just
					// finishing a Start sequence.  Bluetooth GPS
					// devices will NOT disconnect unless you give
					// them some time to connect completely first.
                    Thread.Sleep(1000);
#endif

                    if (!leaveStreamOpen)
                    {
                        // And close the underlying stream
                        if (ParsingStream != null)
                            ParsingStream.Close();

                        // And get rid of the Base Stream
                        if (pBaseStream != null)
                            pBaseStream.Close();
                    }

#if !PocketPC || DesignTime || (PocketPC && Framework20)
                    /* On Desktop Framework and Compact Framework 2.0, we can
                     * Join and forcefully abort threads.
                     */

                    try
                    {
                        if (ParsingThread != null && !ParsingThread.Join(MaximumGracefulShutdownTime))
                        {
                            // Forcefully abort the thread
                            ParsingThread.Abort();

                            #region Compact Framework 2.0 Cleanup
#if PocketPC && Framework20
                            /* On Compact Framework 2.0, Thread.Abort does not cause
                             * "finally" clauses to execute!  So, we must take extra
                             * precautions to ensure that the thread is gracefully
                             * shut down and all variables reset properly.
                             */

                            try
                            {
                                // Signal that parsing has completed
                                if (ParsingWaitHandle.Handle != EmptyIntPtr && pState != ExecutionState.Disposed)
                                    ParsingWaitHandle.Set();
                            }
                            catch
                            { }

#if !Smartphone
                            // Unhook from power events
                            try
                            {
                                PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                                PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                            }
                            catch
                            {
                            }
#endif
#endif
                            #endregion
                        }
                    }
                    catch
                    {
                    }

                    // If we're still stopping, raise events
                    if (pState == ExecutionState.Stopping)
                    {
                        // Signal that we are now stopping
                        pState = ExecutionState.Stopped;

                        // Signal we're disconnected
                        OnDisconnected();
                    }
#else
                    // Wait up to two seconds for a graceful exit
                    for(int i = 0; i < 20; i++)
                    {
                        // Was it exited?
                        if(!pIsParsingThreadAlive)
                            // Yes. 
                            break;

                        // No.  Keep waiting
                        Thread.Sleep(100);
                        Thread.Sleep(0);
                    }

                    // If it's STILL alive, forcefully exit
                    if (pIsParsingThreadAlive)
                    {
                        // That didn't work.  Forcefully abort the thread
                        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnStop));
                    }        
                    else
                    {
#if PocketPC && !Framework20
						// Signal that shutdown is now in progress
						ShutdownCompleteWaitHandle.Set();
#endif

                        OnDisconnected();
                    }
#endif
                }
                // Let other threads get a chance to lock
                Thread.Sleep(0);

#endregion
            }
            else if (pState == ExecutionState.Paused)
            {
                #region The interpreter is currently paused

                // Prevent any state changes until this completes
                lock (StateChangeSyncRoot)
                {
                    // Signal that we are now stopping
                    pState = ExecutionState.Stopping;

                    // Un-pause the interpreter
                    PausingWaitHandle.Set();

#if PocketPC && !Framework20
					// Signal that shutdown is now in progress
					ShutdownCompleteWaitHandle.Reset();
#endif

                    // Raise an event that we're disconnecting
                    OnDisconnecting();

#if PocketPC
					// Wait for a moment just in case we're just
					// finishing a Start sequence.  Bluetooth GPS
					// devices will NOT disconnect unless you give
					// them some time to connect completely first.
                    Thread.Sleep(1000);
#endif

                    // And close the underlying stream
                    if (!leaveStreamOpen)
                    {
                        if (ParsingStream != null)
                            ParsingStream.Close();

                        // And get rid of the Base Stream
                        if (pBaseStream != null)
                            pBaseStream.Close();
                    }

#if !PocketPC || DesignTime || (PocketPC && Framework20)
                    /* On Desktop Framework and Compact Framework 2.0, we can
                     * Join and forcefully abort threads.
                     */

                    try
                    {
                        if (ParsingThread != null && !ParsingThread.Join(MaximumGracefulShutdownTime))
                        {
                            // Forcefully abort the thread
                            ParsingThread.Abort();

                            #region Compact Framework 2.0 Cleanup
#if PocketPC && Framework20
                            /* On Compact Framework 2.0, Thread.Abort does not cause
                             * "finally" clauses to execute!  So, we must take extra
                             * precautions to ensure that the thread is gracefully
                             * shut down and all variables reset properly.
                             */

                            try
                            {
                                // Signal that parsing has completed
                                if (ParsingWaitHandle.Handle != EmptyIntPtr && pState != ExecutionState.Disposed)
                                    ParsingWaitHandle.Set();
                            }
                            catch
                            { }

#if !Smartphone
                            // Unhook from power events
                            try
                            {
                                PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                                PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                            }
                            catch
                            {
                            }
#endif
#endif
                            #endregion
                        }
                    }
                    catch
                    {
                    }

                    // If we're still stopping, raise events
                    if (pState == ExecutionState.Stopping)
                    {
                        // Signal that we are now stopping
                        pState = ExecutionState.Stopped;

                        // Signal we're disconnected
                        OnDisconnected();
                    }
#else
                    // Wait up to two seconds for a graceful exit
                    for(int i = 0; i < 20; i++)
                    {
                        // Was it exited?
                        if(!pIsParsingThreadAlive)
                            // Yes. 
                            break;

                        // No.  Keep waiting
                        Thread.Sleep(100);
                        Thread.Sleep(0);
                    }

                    // If it's STILL alive, forcefully exit
                    if (pIsParsingThreadAlive)
                    {
                        // That didn't work.  Forcefully abort the thread
                        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnStop));
                    }        
                    else
                    {
#if PocketPC && !Framework20
						// Signal that shutdown is now in progress
						ShutdownCompleteWaitHandle.Set();
#endif

                        OnDisconnected();
                    }
#endif
                }
                // Let other threads get a chance to lock
                Thread.Sleep(0);

                #endregion
            }
            else if (pState == ExecutionState.Resuming
				|| pState == ExecutionState.DeviceResuming)
            {
                #region The interpreter is trying to re-connect.

                // Abort all re-connection attempts and shut down
                // Prevent any state changes until this completes
                lock (StateChangeSyncRoot)
                {
                    // Signal that we are now stopping
                    pState = ExecutionState.Stopping;

#if PocketPC && !Framework20
					// Signal that shutdown is now in progress
					ShutdownCompleteWaitHandle.Reset();
#endif

                    // Raise an event that we're disconnecting
                    OnDisconnecting();

#if PocketPC
                    // Wait for a moment just in case we're just
                    // finishing a Start sequence.  Bluetooth GPS
                    // devices will NOT disconnect unless you give
                    // them some time to connect completely first.
                    Thread.Sleep(1000);
#endif

                    // And close the underlying stream
                    if (!leaveStreamOpen)
                    {
                        if (ParsingStream != null)
                            ParsingStream.Close();

                        // And get rid of the Base Stream
                        if (pBaseStream != null)
                            pBaseStream.Close();
                    }

#if !PocketPC || DesignTime || (PocketPC && Framework20)
                    /* On Desktop Framework and Compact Framework 2.0, we can
                     * Join and forcefully abort threads.
                     */

                    try
                    {
                        if (ParsingThread != null && !ParsingThread.Join(MaximumGracefulShutdownTime))
                        {
                            // Forcefully abort the thread
                            ParsingThread.Abort();

                            #region Compact Framework 2.0 Cleanup
#if PocketPC && Framework20
                            /* On Compact Framework 2.0, Thread.Abort does not cause
                             * "finally" clauses to execute!  So, we must take extra
                             * precautions to ensure that the thread is gracefully
                             * shut down and all variables reset properly.
                             */

                            try
                            {
                                // Signal that parsing has completed
                                if (ParsingWaitHandle.Handle != EmptyIntPtr && pState != ExecutionState.Disposed)
                                    ParsingWaitHandle.Set();
                            }
                            catch
                            { }

#if !Smartphone
                            // Unhook from power events
                            try
                            {
                                PowerEvent.DeviceResumed -= new EventHandler(PowerEvent_DeviceResumed);
                                PowerEvent.DeviceSuspended -= new EventHandler(PowerEvent_DeviceSuspended);
                            }
                            catch
                            {
                            }
#endif
#endif
                            #endregion
                        }
                    }
                    catch
                    {
                    }

                    // If we're still stopping, raise events
                    if (pState == ExecutionState.Stopping)
                    {
                        // Signal that we are now stopping
                        pState = ExecutionState.Stopped;

                        // Signal we're disconnected
                        OnDisconnected();
                    }
#else
                    // Wait up to two seconds for a graceful exit
                    for(int i = 0; i < 20; i++)
                    {
                        // Was it exited?
                        if(!pIsParsingThreadAlive)
                            // Yes. 
                            break;

                        // No.  Keep waiting
                        Thread.Sleep(100);
                        Thread.Sleep(0);
                    }

                    // If it's STILL alive, forcefully exit
                    if (pIsParsingThreadAlive)
                    {
                        // That didn't work.  Forcefully abort the thread
                        System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(OnStop));
                    }        
                    else
                    {
#if PocketPC && !Framework20
						// Signal that shutdown is now in progress
						ShutdownCompleteWaitHandle.Set();
#endif

                        OnDisconnected();
                    }
#endif
                }
                // Let other threads get a chance to lock
                Thread.Sleep(0);


                #endregion
            }
            else
            {
                throw new InvalidOperationException("The interpreter is in an unhandled state for termination: " + pState.ToString());
            }
		}

#if PocketPC && !Framework20
        private void OnStop(object state)
        {
            /* On Compact Framework 1.0, we must wait for the parsing thread to 
			 * exit since the thread cannot be aborted.
			 */
			while(pIsParsingThreadAlive)
			{
				Thread.Sleep(100);
				Thread.Sleep(0);
			}


			// And dispose of the shutdown thread
			if(pState != ExecutionState.Disposed)
			{
				// Signal we're disconnected
				OnDisconnected();

				// Signal that we are now stopping
				pState = ExecutionState.Stopped;

				// Signal that shutdown is now finished
				if (ShutdownCompleteWaitHandle.Handle != EmptyIntPtr)
				{
					try
					{
						ShutdownCompleteWaitHandle.Set();
					}
					catch
					{
					}
				}
			}
        }
#endif

        /// <summary>
		/// Halts logging of received data and closes the recording stream.
		/// </summary>
public virtual void StopRecording()
{
    lock (RecordingSyncRoot)
    {
        // If we're not recording and there's no stream to close, exit
        if (!pIsRecording && pRecordingStream == null)
            return;
        // Close the recording stream
        if (pRecordingStream != null)
        {
            // NOTE: Do NOT close the stream!  It was created externally, so it is the responsibility of the caller
            // pRecordingStream.Close();
            pRecordingStream = null;
        }
        // And flag that we're no longer recording
        pIsRecording = false;
    }
}

		/// <summary>Returns the friendly name of the device.</summary>
#if !PocketPC || DesignTime
        [Category("Data")]
        [Description("Returns the friendly name of the device.")]
#endif
        public virtual string DeviceName
		{
			get
			{
				return Protocol + " GPS Device";
			}
		}

		/// <summary>
		/// Returns the type reponsible for reading and writing raw GPS data.
		/// </summary>
#if !PocketPC || DesignTime
		[Category("Data")]
        [Description("Returns the type reponsible for reading and writing raw GPS data.")]
#endif
		public abstract Type ParsingType { get; }

		/// <summary>Controls the stream responsible for reading and writing raw GPS data.</summary>
#if !PocketPC || DesignTime
        [Category("Data")]
        [Description("Controls the stream responsible for reading and writing raw GPS data.")]
#endif
        public abstract Stream ParsingStream { get; }

#if PocketPC 
		#region Power Monitoring events

		/// <summary>
		/// Occurs when a mobile device power-down is imminent.
		/// </summary>
		/// <remarks>This method gives interpreters a brief opportunity to
		/// signal the fact that the device is suspending.  Since the device
		/// will suspend almost immediately after the power button is pressed,
		/// the code executed in this method must be extremely lightweight,
		/// such as setting a boolean to True.  By default, this method changes
		/// the state of the interpreter to <strong>DeviceSuspending</strong>
		/// in order to recover the connection automatically when power
		/// is resumed.</remarks>
		protected virtual void OnDeviceSuspended()
		{
			if (DeviceSuspended != null)
#if !PocketPC || Framework20
                DynamicEvent.BeginInvoke(DeviceSuspended, this, EventArgs.Empty);
#else
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginDeviceSuspended));
#endif
		}

#if PocketPC && !Framework20
		private void BeginDeviceSuspended(object state)
		{
			if(DeviceSuspended != null)
				DeviceSuspended(this, EventArgs.Empty);
		}
#endif

		private void PowerEvent_DeviceSuspended(object sender, EventArgs e)
		{            
            //// Now try to gracefully show suspension.
            lock (StateChangeSyncRoot)
            {
                // Signal that a shutdown is imminent
                OnDisconnecting();

                //// Close the connection immediately!
                //if (pBaseStream != null)
                //    pBaseStream.Close();

                // Signal that we're suspending
                pState = ExecutionState.DeviceSuspending;

                // Signal that we're disconnected
                OnDisconnected();

                // And raise a notification
                OnDeviceSuspended();
            }
			Thread.Sleep(0);
		}

		/// <summary>
		/// Occurs when power to a mobile device has been resumed.
		/// </summary>
		/// <remarks>This method gives interpreters the opportunity to perform
		/// any re-initialization necessary to fully recover from suspending the
		/// device.  By default, the interpreter will call the <strong>Restart</strong>
		/// method to re-establish a connection with the GPS device.</remarks>
		protected virtual void OnDeviceResumed()
		{
            // We must avoid thread marshaling to prevent deadlocks.
            // So, just raise the event
			if (DeviceResumed != null)
#if !PocketPC || Framework20
                DynamicEvent.BeginInvoke(DeviceResumed, this, EventArgs.Empty);
#else
				System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(BeginDeviceResumed));
#endif
        }

#if PocketPC && !Framework20
		private void BeginDeviceResumed(object state)
		{
			if(DeviceResumed != null)
				DeviceResumed(this, EventArgs.Empty);
		}
#endif

        private void PowerEvent_DeviceResumed(object sender, EventArgs e)
		{
            lock (StateChangeSyncRoot)
			{
                pState = ExecutionState.DeviceResuming;

                // Give the user an opportunity to cancel
                OnDeviceResumed();

				// Let the device "boot back up".  Bluetooth needs
				// to re-initialize on the system.
				Thread.Sleep(4000);

                // If we're supporting the GPSID, reset it
                try
                {
                    if (GeoFramework.IO.Devices.AllowSharedGpsDeviceConnections
                        && GeoFramework.IO.SharedGpsDevice.IsSupported)
                    {
                        GeoFramework.IO.Devices.SharedGpsDevice.Refresh();
                    }
                }
                catch
                {}

				// Loop until a resume succeeds
				while (pState == ExecutionState.DeviceResuming)
				{
					try
					{
                        // Signal that we're connecting
                        CancelableEventArgs args = new CancelableEventArgs(false);
                        OnConnecting(args);

                        // Was the connection cancelled?
                        if (args.Canceled)
                        {
                            // Signal that we're disconnected
                            OnDisconnected();

                            // And stop
                            pState = ExecutionState.Stopped;

                            return;
                        }

						// Make a new stream
						BaseStream = GetFreshStream();

						// Force it to open
						long Len = pBaseStream.Length;

						// Signal that we're connecting
						OnConnected();

                        //Application.DoEvents();
                        //Thread.Sleep(1000);

						// And signal that we're running again
						pState = ExecutionState.Running;
					}
					catch
					{
						// Signal that we're disconnecting
						OnDisconnecting();

						// Close the connection immediately!
						if (pBaseStream != null)
							pBaseStream.Close();

						// Signal that we're disconnected
						OnDisconnected();

                        //Application.DoEvents();
                        //Thread.Sleep(1000);

						// Signal that we're still resuming
						pState = ExecutionState.DeviceResuming;
					}
				}
			}
			Thread.Sleep(0);
		}
       
		#endregion
#endif
	}
}
